//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner.Import
{
    #region Using directives

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Media;
    using Microsoft.Research.ScientificWorkflow.TridentComposer;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.ScientificWorkflow.UIDesigner.Controls;
    using Microsoft.Research.DataLayer;
    using Microsoft.Win32;
    using System.Globalization;
    using System.IO.Packaging;
    using System.Windows.Input;
    using Microsoft.Practices.EnterpriseLibrary.Logging;

    #endregion

    /// <summary>
    /// Interaction logic for ImportDialog.xaml
    /// </summary>
    public partial class ImportDialog : Window
    {
        #region Private variables

        /// <summary>
        /// Registry connection object.
        /// </summary>
        private Connection registryConnection;

        /// <summary>
        /// Workflow properties object.
        /// </summary>
        private WorkflowProperties wfProperties;

        /// <summary>
        /// Workflow category picker control.
        /// </summary>
        private CategoryPicker categoryPickerControl;

        /// <summary>
        /// Reference matrix for files that will be imported and their dependencies.
        /// </summary>
        private Collection<FileReference> fileReferences;

        /// <summary>
        /// Dependencies column.
        /// </summary>
        private SortListViewColumn colDependencies;

        /// <summary>
        /// Current import type - Workflow or Activity or TypeInitializer.
        /// </summary>
        private ImportType importType;

        /// <summary>
        /// True if import process was cancelled, False otherwise.
        /// </summary>
        private bool cancelled;

        /// <summary>
        /// Current selected file in references list.
        /// </summary>
        private string selectedReference;

        /// <summary>
        /// Cache for the assemblies. The extension acts as the key and the metadata is the value.
        /// </summary>
        private Dictionary<string, Collection<AssemblyMetadata>> assemblyStore = new Dictionary<string, Collection<AssemblyMetadata>>();

        /// <summary>
        /// The cache for storing the categories.
        /// </summary>
        private Collection<WorkflowCategoryModel> categories;

        /// <summary>
        /// Package Temp directory path.
        /// </summary>
        private string packageTempDirectory;

        /// <summary>
        /// True if this is a Save To Registry operation right after Open From File System.
        /// False if this is any other operation or Save To Registry after Save To File System.
        /// </summary>
        private bool freshSaveToRegistry;

        #endregion

        #region Public properties

        /// <summary>
        /// Gets the file references matrix.
        /// </summary>
        public Collection<FileReference> FileReferences
        {
            get
            {
                return this.fileReferences;
            }
        }

        /// <summary>
        /// Gets the workflow properties.
        /// </summary>
        public WorkflowProperties WorkflowProperty
        {
            get
            {
                return this.wfProperties;
            }
        }

        /// <summary>
        /// Gets a value indicating whether import process was cancelled or not.
        /// </summary>
        public bool Canceled
        {
            get
            {
                return this.cancelled;
            }
        }

        /// <summary>
        /// Gets or sets the package temp directory.
        /// </summary>
        public string PackageTempDirectory
        {
            get
            {
                return this.packageTempDirectory;
            }

            set
            {
                this.packageTempDirectory = value;
            }
        }

        /// <summary>
        /// Gets or sets the path of the selected package. Value valid only if import package is selected.
        /// </summary>
        public string PackagePath { get; private set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes the control.
        /// </summary>
        /// <param name="owner">
        /// Owner window for this control.
        /// </param>
        /// <param name="connection">
        /// Registry connection.
        /// </param>
        /// <param name="type">
        /// Type of import.
        /// </param>
        public ImportDialog(Window owner, Connection connection, ImportType type)
        {
            InitializeComponent();
            if (owner != null)
            {
                this.Owner = owner;
            }
            this.importType = type;
            this.btnDependencies.IsEnabled = false;
            this.btnRemove.IsEnabled = false;

            // Customize UI based on import type.
            if (this.importType == ImportType.Workflow)
            {
                // Show workflow properties and set headers for Workflow Import.
                this.grpWorkflowDetails.Visibility = Visibility.Visible;
                this.wfProperties = new WorkflowProperties();
                this.txtWorkflowName.TextChanged += new TextChangedEventHandler(this.OnWorkflowNameChanged);
                this.btnCategoryBrowse.Click += new RoutedEventHandler(this.OnBrowseCategory);
                this.txtTitle.Text = TridentResourceManager.GetString("ImportWorkflow");
                this.grpAssociatedFiles.Header = TridentResourceManager.GetString("WorkflowDependencies");
                this.grpAssociatedFiles.Height = 280;
            }
            else if (this.importType == ImportType.WorkflowPackage)
            {
                // Show workflow properties and set headers for Workflow Package Import.
                this.grpWorkflowDetails.Visibility = Visibility.Visible;
                this.wfProperties = new WorkflowProperties();
                this.txtWorkflowName.TextChanged += new TextChangedEventHandler(this.OnWorkflowNameChanged);
                this.btnCategoryBrowse.Click += new RoutedEventHandler(this.OnBrowseCategory);
                this.txtTitle.Text = TridentResourceManager.GetString("ImportWorkflowPackage");
                this.grpAssociatedFiles.Header = TridentResourceManager.GetString("WorkflowDependencies");
                this.grpAssociatedFiles.Height = 280;
            }
            else if (this.importType == ImportType.Activity)
            {
                // Set headers for Activity Import.
                this.txtTitle.Text = TridentResourceManager.GetString("ImportActivity");
                this.grpAssociatedFiles.Header = TridentResourceManager.GetString("ActivityDependencies");
                this.grpAssociatedFiles.Height = 455;
            }
            else if (this.importType == ImportType.OpenFromFileSystem)
            {
                // Set headers for Open workflow file file system.
                this.txtTitle.Text = TridentResourceManager.GetString("OpenWorkflow");
                this.btnImport.Content = TridentResourceManager.GetString("OpenWorkflowButton");
                this.grpAssociatedFiles.Header = TridentResourceManager.GetString("WorkflowDependencies");
                this.grpAssociatedFiles.Height = 455;

                // Hide Operation column.
                GridView gridView = this.lstDependencies.View as GridView;
                if (gridView != null)
                {
                    gridView.Columns.Remove(this.colOperation);
                }
            }
            else if (this.importType == ImportType.SaveToRegistry)
            {
                // Set headers for Open workflow file file system.
                this.txtTitle.Text = TridentResourceManager.GetString("SaveWorkflow");
                this.btnImport.Content = TridentResourceManager.GetString("NextButton");
                this.grpAssociatedFiles.Header = TridentResourceManager.GetString("WorkflowDependencies");
                this.grpAssociatedFiles.Height = 455;

                // Diable browse button so that the XOML cannot be changed.
                this.btnBrowse.IsEnabled = false;

                // Enable Show Dependencies button.
                this.btnDependencies.IsEnabled = true;
            }

            // Instantiate file references matrix.
            this.fileReferences = new Collection<FileReference>();
            this.registryConnection = connection;

            // Associate event handlers.
            this.btnImport.Click += new RoutedEventHandler(this.OnImport);
            this.btnCancel.Click += new RoutedEventHandler(this.OnCancel);
            this.btnClose.Click += new RoutedEventHandler(this.OnCancel);
            this.btnBrowse.Click += new RoutedEventHandler(this.OnBrowse);
            this.btnDependencies.Click += new RoutedEventHandler(this.OnDependenciesClicked);
            this.btnRemove.Click += new RoutedEventHandler(this.OnRemoveFile);
            this.lstDependencies.SelectionChanged += new SelectionChangedEventHandler(this.OnFileSelectionChanged);
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Loads the given files in file list.
        /// </summary>
        /// <param name="references">
        /// List of file references.
        /// </param>
        public void LoadFiles(Collection<FileReference> references)
        {
            if (references != null && references.Count > 0)
            {
                this.fileReferences = references;
                this.txtLocation.Text = Path.GetDirectoryName(this.fileReferences[0].FilePath);

                if (this.importType == ImportType.SaveToRegistry)
                {
                    if (this.fileReferences != null && this.fileReferences.Count == 1 && Helper.IsWorkflowFile(this.fileReferences[0].FilePath))
                    {
                        this.freshSaveToRegistry = false;
                    }
                    else
                    {
                        this.freshSaveToRegistry = true;
                    }
                }

                // Refresh UI to display the reference matrix.
                this.RefreshFileList();
            }
        }

        #endregion

        #region Private event handlers

        /// <summary>
        /// Fired when user clicks Browse to select files to be imported.
        /// Brings up the Open FileDialog.
        /// </summary>
        /// <param name="sender">
        /// Browse button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnBrowse(object sender, RoutedEventArgs e)
        {
            try
            {
                // Get the selected files.
                Collection<string> selectedFiles = this.SelectFilesFromDisk(false);
                if (selectedFiles != null && selectedFiles.Count > 0)
                {
                    this.Cursor = Cursors.Wait;

                    // Set location to parent folder.
                    this.txtLocation.Text = Path.GetDirectoryName(selectedFiles[0]);
                    this.dckLocationError.Visibility = Visibility.Collapsed;
                    this.btnDependencies.IsEnabled = true;
                    this.btnRemove.IsEnabled = true;

                    if (this.fileReferences != null)
                    {
                        this.fileReferences.Clear();
                    }

                    // Get the references by loading the Dlls in another AppDomain.
                    BuildReferencesProxy proxy = new BuildReferencesProxy(this.fileReferences, selectedFiles, this.txtLocation.Text);
                    Collection<FileReference> filerefs = proxy.Execute();

                    // Add the file references to the collection. This is needed as serialization across appdomains creates new objects.
                    foreach (FileReference fileRef in filerefs)
                    {
                        this.fileReferences.Add(fileRef);
                    }

                    // Check if the files are already present in the registry.
                    this.CheckIfReferencesPresentRegistry(this.fileReferences);

                    ImportDialog.UpdateUnmanagedReferences(this.fileReferences);

                    // Refresh UI to display the reference matrix.
                    this.RefreshFileList();
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(ex);
            }
            finally
            {
                this.Cursor = Cursors.Arrow;
            }
        }

        /// <summary>
        /// Fired when user changes workflow name.
        /// </summary>
        /// <param name="sender">
        /// Workflow name textbox.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnWorkflowNameChanged(object sender, TextChangedEventArgs e)
        {
            // Store name of the workflow.
            string name = this.txtWorkflowName.Text.Trim();
            if (!name.Equals(this.wfProperties.Name, StringComparison.OrdinalIgnoreCase))
            {
                this.wfProperties.Name = name;
                this.dckWorkflowNameError.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// Fired when user clicks on Browse category.
        /// Brings up the category picker control.
        /// </summary>
        /// <param name="sender">
        /// Browse category button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnBrowseCategory(object sender, RoutedEventArgs e)
        {
            try
            {
                // Show the existing categories list.
                if (this.categories == null)
                {
                    this.categories =
                        CategoriesComposer.ReadWorkflowCategoriesFromRegistry(this.registryConnection, TridentAuthentication.LoggedInUser, false);
                }

                this.categoryPickerControl = new CategoryPicker(categories);
                this.categoryPickerControl.CategoryPickerOk += new EventHandler(this.OnCategoryPickerOk);
                this.categoryPickerControl.CategoryPickerCancel += new EventHandler(this.OnCategoryPickerCancel);

                Window categoryPickerWindow = new Window();
                this.categoryPickerControl.HeaderDrag += (sender1, e1) => categoryPickerWindow.DragMove();

                categoryPickerWindow.Content = this.categoryPickerControl;
                categoryPickerWindow.SizeToContent = SizeToContent.WidthAndHeight;
                categoryPickerWindow.ShowInTaskbar = false;
                categoryPickerWindow.MinWidth = 250;
                categoryPickerWindow.MinHeight = 300;
                categoryPickerWindow.MaxHeight = 300;
                categoryPickerWindow.MaxWidth = 250;
                categoryPickerWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                categoryPickerWindow.WindowStyle = WindowStyle.None;
                categoryPickerWindow.BorderBrush = new SolidColorBrush(Colors.Transparent);
                categoryPickerWindow.BorderThickness = new Thickness(0, 0, 0, 0);
                categoryPickerWindow.ResizeMode = ResizeMode.NoResize;
                categoryPickerWindow.ShowDialog();
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleKnownExceptionsInUI(ex);
            }
        }

        /// <summary>
        /// Fired when user clicks Ok in category picker.
        /// </summary>
        /// <param name="sender">
        /// Category picker control.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnCategoryPickerOk(object sender, EventArgs e)
        {
            Window categoryWindow = Window.GetWindow(this.categoryPickerControl);
            if (categoryWindow != null)
            {
                categoryWindow.Close();
            }
            if (this.categoryPickerControl.SelectedModel != null)
            {
                this.txtCategory.Text = this.categoryPickerControl.SelectedModel.Label;
                this.wfProperties.CategoryId = this.categoryPickerControl.SelectedModel.Id;
                this.dckCategoryError.Visibility = Visibility.Collapsed;
            }
        }

        /// <summary>
        /// Fired when user clicks Cancel in category picker.
        /// </summary>
        /// <param name="sender">
        /// Category picker control.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnCategoryPickerCancel(object sender, EventArgs e)
        {
            Window categoryWindow = Window.GetWindow(this.categoryPickerControl);
            if (categoryWindow != null)
            {
                categoryWindow.Close();
            }
        }

        /// <summary>
        /// Removes a file from the reference matrix.
        /// </summary>
        /// <param name="sender">
        /// Remove button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnRemoveFile(object sender, RoutedEventArgs e)
        {
            // Get the selected file.
            FileReference fileRef = this.lstDependencies.SelectedItem as FileReference;
            if (fileRef != null)
            {
                // Get the list of files that refer to this file.
                string fileName = Helper.GetFileName(fileRef.FilePath);
                IEnumerable<FileReference> references =
                    this.fileReferences.Where(file => file.DisplayReferences.Contains(fileName));
                foreach (FileReference reference in references)
                {
                    // Disassociate this reference.
                    reference.References.Remove(
                        reference.References.SingleOrDefault(item => Helper.GetFileName(item).Equals(fileName, StringComparison.OrdinalIgnoreCase)));
                    reference.DisplayReferences.Remove(fileName);
                    if (reference.References.Count <= 0)
                    {
                        // If this was the last file in references list, hide the list box and remove button.
                        reference.ReferenceVisibility = Visibility.Collapsed;
                    }
                }

                // Delete this file.
                this.fileReferences.Remove(fileRef);

                if (fileReferences.Count == 0)
                {
                    this.txtLocation.Text = string.Empty;
                }

                // Refresh UI to display the reference matrix.
                this.RefreshFileList();
            }
        }

        /// <summary>
        /// Fired when user clicks on dependencies option.
        /// </summary>
        /// <param name="sender">
        /// Advanced Import button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnDependenciesClicked(object sender, EventArgs e)
        {
            if (this.btnDependencies.Tag.ToString().Equals("Show", StringComparison.OrdinalIgnoreCase))
            {
                // Toggle this to Hide Dependencies.
                this.btnDependencies.Content = TridentResourceManager.GetString("HideDependencies");
                this.btnDependencies.Tag = "Hide";

                this.colFile.Width = 150;
                this.colOperation.Width = 320;

                // Add a new column called References.
                if (this.colDependencies == null)
                {
                    this.colDependencies = new SortListViewColumn();
                    this.colDependencies.Header = TridentResourceManager.GetString("DependenciesColumnHeader");
                    this.colDependencies.MinimumWidth = 260;
                    this.colDependencies.Width = 260;
                    if (this.importType == ImportType.SaveToRegistry && this.freshSaveToRegistry)
                    {
                        this.colDependencies.CellTemplate = this.FindResource("ReferencesTemplateForSaveToRegistry") as DataTemplate;
                    }
                    else
                    {
                        this.colDependencies.CellTemplate = this.FindResource("ReferencesTemplate") as DataTemplate;
                    }
                }

                GridView gridView = this.lstDependencies.View as GridView;
                if (gridView != null)
                {
                    gridView.Columns.Add(this.colDependencies);
                }
            }
            else
            {
                // Toggle this to Show Dependencies.
                this.btnDependencies.Content = TridentResourceManager.GetString("ShowDependencies");
                this.btnDependencies.Tag = "Show";

                // Remove the dependencies column.
                GridView gridView = this.lstDependencies.View as GridView;
                if (gridView != null)
                {
                    gridView.Columns.Remove(this.colDependencies);
                }
            }

            // Refresh UI to display the reference matrix.
            this.RefreshFileList();
        }

        /// <summary>
        /// Fired when user clicks on Add to add a reference to the file.
        /// </summary>
        /// <param name="sender">
        /// Add reference button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnAddReference(object sender, RoutedEventArgs e)
        {
            // Add button will have file path in its Tag.
            // Get the FileReference for this file path.
            Button addButton = sender as Button;
            FileReference fileRef =
                this.fileReferences.FirstOrDefault(file => file.FilePath.Equals(addButton.Tag.ToString(), StringComparison.OrdinalIgnoreCase));

            if (fileRef != null)
            {
                // Get the selected files.
                Collection<string> selectedFiles = this.SelectFilesFromDisk(true);

                // Iterate through the files to check if the selection already exists in the list.
                for (int fileCount = 0; fileCount < selectedFiles.Count; fileCount++)
                {
                    string currentFile = selectedFiles[fileCount];
                    string fileName = Helper.GetFileName(currentFile);

                    // Check if the selected file is same as the main file.
                    if (!fileName.Equals(fileRef.DisplayName, StringComparison.OrdinalIgnoreCase))
                    {
                        // Check if the selected file is already in references list.
                        if (!fileRef.DisplayReferences.Contains(fileName))
                        {
                            // Add this new file to references collection of the corresponding FileReference object.
                            fileRef.References.Add(currentFile);
                            fileRef.DisplayReferences.Add(fileName);
                            fileRef.ReferenceVisibility = Visibility.Visible;
                        }
                        else
                        {
                            // Remove the file from the list.
                            selectedFiles.Remove(currentFile);
                            fileCount--;

                            // Reference already exists. Show an error message.
                            string message = string.Format(CultureInfo.InvariantCulture, TridentResourceManager.GetString("ImportReferenceExists"), currentFile);
                            TridentMessageBox.ShowTridentErrorMessageBox(this, message);
                        }
                    }
                    else
                    {
                        // Remove the file from the list.
                        selectedFiles.Remove(currentFile);
                        fileCount--;

                        // Reference is same as main file. Show an error message.
                        string message = string.Format(CultureInfo.InvariantCulture, TridentResourceManager.GetString("ImportReferenceExists"), currentFile);
                        TridentMessageBox.ShowTridentErrorMessageBox(this, message);
                    }
                }

                if (selectedFiles.Count > 0)
                {
                    try
                    {
                        string dirPath = Path.GetDirectoryName(selectedFiles[0]);

                        // Get the references by loading the assemblies in another appdomain.
                        BuildReferencesProxy proxy = new BuildReferencesProxy(this.fileReferences, selectedFiles, dirPath);
                        Collection<FileReference> filerefs = proxy.Execute();

                        this.fileReferences.Clear();
                        foreach (FileReference fileReference in filerefs)
                        {
                            this.fileReferences.Add(fileReference);
                        }

                        // Check if the files are already present in the registry.
                        this.CheckIfReferencesPresentRegistry(this.fileReferences);

                        // Refresh UI to display the reference matrix.
                        this.RefreshFileList();
                    }
                    catch (TridentCustomException ex)
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
                    }
                    catch (Exception ex)
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
                    }
                }
            }
        }

        /// <summary>
        /// Fired when user clicks Remove to remove a reference.
        /// </summary>
        /// <param name="sender">
        /// Remove reference button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnRemoveReference(object sender, RoutedEventArgs e)
        {
            // Remove button will have file path in its Tag.
            // Get the FileReference for this file path.
            Button removeButton = sender as Button;
            FileReference fileRef =
                this.fileReferences.FirstOrDefault(D => D.FilePath.Equals(removeButton.Tag.ToString(), StringComparison.OrdinalIgnoreCase));
            if (fileRef != null)
            {
                // Get the selected reference.
                int refIndex = fileRef.DisplayReferences.IndexOf(this.selectedReference);
                if (refIndex >= 0)
                {
                    // Remove this reference.
                    fileRef.References.RemoveAt(refIndex);
                    fileRef.DisplayReferences.RemoveAt(refIndex);
                    if (fileRef.References.Count <= 0)
                    {
                        // If this was the last reference, hide list box and remove button.
                        fileRef.ReferenceVisibility = Visibility.Collapsed;
                    }

                    // Refresh UI to display the reference matrix.
                    this.RefreshFileList();
                }
            }
        }

        /// <summary>
        /// Fired when user selects a reference in the references list box.
        /// </summary>
        /// <param name="sender">
        /// References list box.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnReferenceSelected(object sender, RoutedEventArgs e)
        {
            // Listbox will have file path in its Tag.
            // Get the FileReference for this file path.
            ListBox listBox = sender as ListBox;
            if (listBox.SelectedIndex >= 0)
            {
                // Store the selected reference.
                this.selectedReference = listBox.SelectedItem.ToString();
            }
        }

        /// <summary>
        /// Fired when a file row is selected.
        /// Disables Remove button if this is a workflow file.
        /// </summary>
        /// <param name="sender">
        /// File ListView.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnFileSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            FileReference fileRef = this.lstDependencies.SelectedItem as FileReference;
            if (fileRef != null)
            {
                if (Helper.IsWorkflowFile(fileRef.FilePath) ||
                    (this.importType == ImportType.SaveToRegistry && this.freshSaveToRegistry))
                {
                    this.btnRemove.IsEnabled = false;
                }
                else
                {
                    this.btnRemove.IsEnabled = true;
                }
            }
            else
            {
                this.btnRemove.IsEnabled = false;
            }
        }

        /// <summary>
        /// Fired when user clicks on Import.
        /// </summary>
        /// <param name="sender">
        /// Import button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnImport(object sender, RoutedEventArgs e)
        {
            // Check if all necessary inputs have been provided and are valid.
            bool isValid = this.ValidateUserInputs();
            if (isValid)
            {
                // Check if any files need import.
                if (this.importType == ImportType.Activity)
                {
                    if (!this.fileReferences.Any(f => f.ContainsActivity))
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(TridentResourceManager.GetString("ErrorNoActivitiesFoundInAssembly"));
                        this.cancelled = true;
                    }
                    else if (!this.cancelled && !this.CanContinueWithImport())
                    {
                        TridentMessageBox.ShowTridentErrorMessageBox(TridentResourceManager.GetString("AllAssembliesExist"));
                        this.cancelled = true;
                    }
                    else
                    {
                        this.cancelled = false;
                    }
                }
                else
                {
                    // If valid, set cancelled to false and close the dialog.
                    this.cancelled = false;
                }
                this.Close();
            }
            else
            {
                // If not, show an error message.
                this.cancelled = true;
                TridentMessageBox.ShowTridentErrorMessageBox(this, TridentResourceManager.GetString("ImportValidationError"));
            }
        }

        /// <summary>
        /// Fired when user clicks on Cancel import.
        /// </summary>
        /// <param name="sender">
        /// Cancel button.
        /// </param>
        /// <param name="e">
        /// Attributes of the event.
        /// </param>
        private void OnCancel(object sender, RoutedEventArgs e)
        {
            // Set cancelled to true and close the dialog.
            this.cancelled = true;
            this.Close();
        }

        /// <summary>
        /// Event handler for the "update exisiting" checkbox check.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">Event args.</param>
        private void CheckBoxChecked(object sender, RoutedEventArgs e)
        {
            try
            {
                Collection<AssemblyMetadata> assemblyMetadataCollection = null;
                CheckBox updateExistingCheck = sender as CheckBox;
                FileReference currentFile = updateExistingCheck.DataContext as FileReference;
                BindingExpression bindingExp = updateExistingCheck.GetBindingExpression(CheckBox.IsCheckedProperty);

                // If workflow file show the workflow tree to select which workflow to update.
                if (Helper.IsWorkflowFile(currentFile.FilePath))
                {
                    // Get categories if not fetched yet.
                    if (this.categories == null)
                    {
                        this.categories =
                            CategoriesComposer.ReadWorkflowCategoriesFromRegistry(this.registryConnection, TridentAuthentication.LoggedInUser, false);
                    }

                    // Show the workflow picker window.
                    WorkflowPicker workflowPicker = new WorkflowPicker(this.categories);
                    workflowPicker.Owner = this;
                    bool dialogResult = (bool)workflowPicker.ShowDialog();

                    // If the user has not seleced cancel then set the workflow to update.
                    if (dialogResult)
                    {
                        if (importType == ImportType.Workflow || importType == ImportType.WorkflowPackage)
                        {
                            if (MessageBoxResult.Yes == TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(TridentResourceManager.GetString("OverrideWorkflowMetadata")))
                            {
                                this.txtWorkflowName.Text = workflowPicker.SelectedWorkflow.Name;
                                this.txtWorkflowDesc.Text = workflowPicker.SelectedWorkflow.Description;
                                this.txtCategory.Text = workflowPicker.SelectedWorkflow.ParentCategoryName;
                                this.wfProperties.CategoryId = workflowPicker.SelectedWorkflow.ParentCategoryId;
                            }
                        }

                        currentFile.AssemblyInRegistryToUpdate = new AssemblyMetadata()
                        {
                            Id = workflowPicker.SelectedWorkflow.Id,
                            Name = workflowPicker.SelectedWorkflow.DisplayName,
                            ImportedBy = workflowPicker.SelectedWorkflow.CreatedBy,
                            ImportedTime = workflowPicker.SelectedWorkflow.CreatedTime.ToString()
                        };

                        bindingExp.UpdateSource();
                    }
                    else
                    {
                        currentFile.UpdateExisting = false;
                        bindingExp.UpdateTarget();
                    }
                }
                // If not assembly file then show the user the images already present in the registry.
                else
                {
                    // Get the extension of the file.
                    string fileExtension = Path.GetExtension(currentFile.FilePath);

                    // Check if the files of the particular extension is already stored in the cache.
                    if (this.assemblyStore.ContainsKey(fileExtension))
                    {
                        Collection<AssemblyMetadata> tempAssemblyMetadata = this.assemblyStore[fileExtension];
                        assemblyMetadataCollection = new Collection<AssemblyMetadata>(tempAssemblyMetadata.ToList());
                    }
                    else
                    {
                        // Else retrieve it from registry.
                        Collection<AssemblyMetadata> tempAssemblyMetadata = Helper.GetAssembliesFromRegistry(fileExtension, this.registryConnection);
                        this.assemblyStore.Add(fileExtension, tempAssemblyMetadata);
                        assemblyMetadataCollection = new Collection<AssemblyMetadata>(tempAssemblyMetadata.ToList());
                    }

                    // Display the dialog where he can select the assembly to update.
                    AssemblySelectDialog assemblySelectDialog = new AssemblySelectDialog(assemblyMetadataCollection);
                    assemblySelectDialog.Owner = this;
                    bool dialogResult = (bool)assemblySelectDialog.ShowDialog();
                    if (dialogResult == true)
                    {
                        currentFile.AssemblyInRegistryToUpdate = assemblySelectDialog.SelectedAssembly;
                        bindingExp.UpdateSource();
                    }
                    else
                    {
                        currentFile.UpdateExisting = false;
                        bindingExp.UpdateTarget();
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentMessageBox.ShowTridentErrorMessageBox(ex.Message);
            }
        }

        /// <summary>
        /// Event handler for the "update exisiting" checkbox unchecked event.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">Event args.</param>
        private void CheckBoxUnchecked(object sender, RoutedEventArgs e)
        {
            CheckBox updateExistingCheck = sender as CheckBox;
            BindingExpression bindingExp = updateExistingCheck.GetBindingExpression(CheckBox.IsCheckedProperty);
            bindingExp.UpdateSource();
        }

        /// <summary>
        /// Event handler for the change assembly click.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">Event args.</param>
        private void ChangeClick(object sender, RoutedEventArgs e)
        {
            Button changeButton = sender as Button;
            FileReference selectedFileReference = changeButton.DataContext as FileReference;
            Collection<AssemblyMetadata> assemblyMetadataCollection = null;

            // If the file is not a workflow file, show the images present in the registry.
            if (!Helper.IsWorkflowFile(selectedFileReference.FilePath))
            {
                // Get the extension of the file.
                string fileExtension = Path.GetExtension(selectedFileReference.FilePath);

                // Check if the files of the particular extension is already stored in the cache.
                if (this.assemblyStore.ContainsKey(fileExtension))
                {
                    Collection<AssemblyMetadata> tempAssemblyMetadata = this.assemblyStore[fileExtension];
                    assemblyMetadataCollection = new Collection<AssemblyMetadata>(tempAssemblyMetadata.ToList());
                }
                else
                {
                    // Else retrieve it from registry.
                    Collection<AssemblyMetadata> tempAssemblyMetadata = Helper.GetAssembliesFromRegistry(fileExtension, this.registryConnection);
                    this.assemblyStore.Add(fileExtension, tempAssemblyMetadata);
                    assemblyMetadataCollection = new Collection<AssemblyMetadata>(tempAssemblyMetadata.ToList());
                }

                // Display the dialog where he can select the assembly to update.
                AssemblySelectDialog assemblySelectDialog = new AssemblySelectDialog(assemblyMetadataCollection);
                assemblySelectDialog.Owner = this;
                bool dialogResult = (bool)assemblySelectDialog.ShowDialog();
                if (dialogResult == true)
                {
                    selectedFileReference.AssemblyInRegistryToUpdate = assemblySelectDialog.SelectedAssembly;
                }
            }
            else
            {
                // Get categories if not fetched yet.
                if (this.categories == null)
                {
                    this.categories =
                        CategoriesComposer.ReadWorkflowCategoriesFromRegistry(this.registryConnection, TridentAuthentication.LoggedInUser, false);
                }

                // If file is a workflow show the workflow picker window.
                WorkflowPicker workflowPicker = new WorkflowPicker(this.categories);
                workflowPicker.Owner = this;
                bool dialogResult = (bool)workflowPicker.ShowDialog();

                // If the user has not seleced cancel then set the workflow to update.
                if (dialogResult)
                {
                    if (importType == ImportType.Workflow || importType == ImportType.WorkflowPackage)
                    {
                        if (MessageBoxResult.Yes == TridentMessageBox.ShowTridentConfirmationYesNoMessageBox(TridentResourceManager.GetString("OverrideWorkflowMetadata")))
                        {
                            this.txtWorkflowName.Text = workflowPicker.SelectedWorkflow.Name;
                            this.txtWorkflowDesc.Text = workflowPicker.SelectedWorkflow.Description;
                            this.txtCategory.Text = workflowPicker.SelectedWorkflow.ParentCategoryName;
                            this.wfProperties.CategoryId = workflowPicker.SelectedWorkflow.ParentCategoryId;
                        }
                    }

                    selectedFileReference.AssemblyInRegistryToUpdate = new AssemblyMetadata()
                    {
                        Id = workflowPicker.SelectedWorkflow.Id,
                        Name = workflowPicker.SelectedWorkflow.DisplayName,
                        ImportedBy = workflowPicker.SelectedWorkflow.CreatedBy,
                        ImportedTime = workflowPicker.SelectedWorkflow.CreatedTime.ToString()
                    };
                }
            }
        }

        /// <summary>
        /// Event handler for the left mouse down on the header of the window. Helps in Dragging the window around.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">Event arguments.</param>
        private void BorderMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Brings up OpenFileDialog and lets the user select file(s) from disk for importing.
        /// </summary>
        /// <param name="disallowWorkflowFiles">
        /// True if selecting workflow files has to be disallowed, False otherwise.
        /// </param>
        /// <returns>
        /// Collection of file paths selected.
        /// </returns>
        private Collection<string> SelectFilesFromDisk(bool disallowWorkflowFiles)
        {
            Collection<string> selectedFiles = new Collection<string>();
            OpenFileDialog openDialog = new OpenFileDialog();

            if (disallowWorkflowFiles)
            {
                openDialog.Filter = "References |*.*";
                openDialog.Multiselect = true;
            }
            else
            {
                // Set the file filter based on current import type.
                if (this.importType == ImportType.Workflow || this.importType == ImportType.OpenFromFileSystem)
                {
                    openDialog.Filter = "Workflows and associated files |*.*";
                    openDialog.Multiselect = true;
                }
                else if (this.importType == ImportType.Activity)
                {
                    openDialog.Filter = "Activity assemblies |*.dll; *.exe";
                }
                else if (this.importType == ImportType.WorkflowPackage)
                {
                    openDialog.Filter = "Workflow Package |*.twp";
                }
            }

            openDialog.CheckFileExists = true;
            openDialog.CheckPathExists = true;
            openDialog.RestoreDirectory = true;
            if ((bool)openDialog.ShowDialog())
            {
                // Get the selected file(s).
                selectedFiles = new Collection<string>(openDialog.FileNames.ToList());

                // If this is workflow package, extract XOML and other files from the package.
                if (this.importType == ImportType.WorkflowPackage && !disallowWorkflowFiles)
                {
                    this.PackagePath = selectedFiles[0];
                    selectedFiles = Helper.ExtractPackage(selectedFiles[0], this.packageTempDirectory, false);
                }

                if (selectedFiles != null)
                {
                    // Do some sanity checks if this is workflow import.
                    if (this.importType == ImportType.Workflow ||
                        this.importType == ImportType.WorkflowPackage ||
                        this.importType == ImportType.OpenFromFileSystem)
                    {
                        // Get the number of XOML files (workflows) selected.
                        int numberOfXomls = selectedFiles.Count(A => Helper.IsWorkflowFile(A));
                        if (disallowWorkflowFiles)
                        {
                            // If selecting workflow files is disallowed and a workflow is selected, show an error message.
                            if (numberOfXomls > 0)
                            {
                                TridentMessageBox.ShowTridentErrorMessageBox(this,
                                    TridentResourceManager.GetString("ImportMoreThanOneWorkflowError"));

                                // Restart select files process.
                                selectedFiles = this.SelectFilesFromDisk(disallowWorkflowFiles);
                            }
                        }
                        else
                        {
                            if (numberOfXomls == 0)
                            {
                                // No workflow selected.
                                TridentMessageBox.ShowTridentErrorMessageBox(this,
                                    TridentResourceManager.GetString("SelectWorkflowFileError"));

                                // Restart select files process.
                                selectedFiles = this.SelectFilesFromDisk(disallowWorkflowFiles);
                            }
                            else if (numberOfXomls != 1)
                            {
                                // If selecting workflows is allowed but more than one workflow is selected, show an error message.
                                TridentMessageBox.ShowTridentErrorMessageBox(this,
                                    TridentResourceManager.GetString("ImportMoreThanOneWorkflowError"));

                                // Restart select files process.
                                selectedFiles = this.SelectFilesFromDisk(disallowWorkflowFiles);
                            }
                        }
                    }
                }
            }

            // All fine. Return selected files.
            return selectedFiles;
        }

        /// <summary>
        /// Validates inputs given by user.
        /// </summary>
        /// <returns>
        /// True if all inputs are valid, False otherwise.
        /// </returns>
        private bool ValidateUserInputs()
        {
            bool isValid = true;
            if (this.importType == ImportType.Workflow || this.importType == ImportType.WorkflowPackage)
            {
                if (this.wfProperties == null)
                {
                    this.wfProperties = new WorkflowProperties();
                }

                // Workflow name.
                string name = this.txtWorkflowName.Text.Trim();
                if (string.IsNullOrEmpty(name) || !TridentWorkflowModel.CheckNameForInvalidCharacters(name))
                {
                    isValid = false;
                    this.dckWorkflowNameError.ToolTip = TridentResourceManager.GetString("EnterNameForWorkflow");
                    this.dckWorkflowNameError.Visibility = Visibility.Visible;
                }
                else
                {
                    this.wfProperties.Name = name;
                }

                // Workflow description.
                this.wfProperties.Description = this.txtWorkflowDesc.Text.Trim();

                if (isValid)
                {
                    // Workflow category.
                    string category = this.txtCategory.Text.Trim();
                    if (string.IsNullOrEmpty(category))
                    {
                        isValid = false;
                        this.dckCategoryError.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        Guid workflowToUpdate = Guid.Empty;
                        FileReference workflowFile = this.fileReferences.FirstOrDefault(f => Helper.IsWorkflowFile(f.FilePath));
                        if (workflowFile != null && workflowFile.UpdateExisting)
                        {
                            workflowToUpdate = workflowFile.AssemblyInRegistryToUpdate.Id;
                        }

                        // Check if the name is unique in the category chosen.
                        TridentWorkflowModel wfModel = new TridentWorkflowModel();
                        if (wfModel != null && !this.wfProperties.CategoryId.Equals(Guid.Empty))
                        {
                            bool validName = false;
                            if (workflowToUpdate.Equals(Guid.Empty))
                            {
                                validName = CategoriesComposer.ValidateWorkflowName(this.wfProperties.CategoryId,
                                    this.wfProperties.Name.Trim(),
                                    this.registryConnection);
                            }
                            else
                            {
                                validName = CategoriesComposer.ValidateWorkflowName(this.wfProperties.CategoryId,
                                    workflowToUpdate,
                                    this.wfProperties.Name.Trim(),
                                    this.registryConnection);
                            }

                            if (!validName)
                            {
                                isValid = false;
                                this.dckWorkflowNameError.ToolTip = TridentResourceManager.GetString("EnterUniqueNameForWorkflow");
                                this.dckWorkflowNameError.Visibility = Visibility.Visible;
                            }
                        }
                    }
                }

                // Interative or not.
                if (this.cmbWorkflowType.SelectedIndex == 0)
                {
                    this.WorkflowProperty.IsInteractive = true;
                }
                else if (this.cmbWorkflowType.SelectedIndex == 1)
                {
                    this.WorkflowProperty.IsInteractive = false;
                }
            }

            // Location of file(s).
            string location = this.txtLocation.Text.Trim();
            if (string.IsNullOrEmpty(location))
            {
                isValid = false;
                if (this.importType == ImportType.Workflow || this.importType == ImportType.OpenFromFileSystem)
                {
                    this.dckLocationError.ToolTip = TridentResourceManager.GetString("SelectWorkflowLocation");
                }
                else if (this.importType == ImportType.Activity)
                {
                    this.dckLocationError.ToolTip = TridentResourceManager.GetString("SelectActivityLocation");
                }

                this.dckLocationError.Visibility = Visibility.Visible;
            }

            return isValid;
        }

        /// <summary>
        /// Check if activities need import.
        /// </summary>
        /// <returns>True if any new files exist; False otherwise.</returns>
        private bool CanContinueWithImport()
        {
            try
            {
                FileReference activityReference = this.fileReferences.FirstOrDefault(f => f.ContainsActivity);
                Collection<FileReference> associatedFiles = new Collection<FileReference>();
                FileReference.GetAllAssociatedFiles(activityReference, this.fileReferences, associatedFiles);

                // If the activity file itself is one of the references then remove the file.
                FileReference duplicateActivityFile =
                    associatedFiles.FirstOrDefault(a => (a.Checksum != null && a.Checksum.Equals(activityReference.Checksum, StringComparison.Ordinal)));

                if (duplicateActivityFile != null)
                {
                    associatedFiles.Remove(duplicateActivityFile);
                }

                return ImportActivity.CheckIfImportRequired(activityReference, associatedFiles, this.registryConnection);
            }
            catch
            {
                // Do nothing.
            }
            return false;
        }

        /// <summary>
        /// Refresh list box to show new data.
        /// </summary>
        private void RefreshFileList()
        {
            this.lstDependencies.ItemsSource = null;
            this.lstDependencies.ItemsSource = this.fileReferences;
        }

        /// <summary>
        /// Check if the file references exist in registry.
        /// </summary>
        /// <param name="fileReferences">The file references.</param>
        private void CheckIfReferencesPresentRegistry(Collection<FileReference> fileReferences)
        {
            foreach (FileReference file in fileReferences)
            {
                if (!file.CopyExistsInRegistry.HasValue)
                {
                    if (!Helper.IsWorkflowFile(file.FilePath))
                    {
                        // If the image is already in the registry store the Image ID so that it will be easier to retrieve the image.
                        Helper.GetImageFromRegistry(file, this.registryConnection);
                    }
                    else
                    {
                        file.CopyExistsInRegistry = false;
                    }
                }
            }
        }

        /// <summary>
        /// This function is used to update the selected and unmanaged assemblies and files.
        /// Approach:
        ///     Get all unreferenced assemblies(files) in FileReferences.
        ///     Then add those unreferenced assemblies(files) to all teh activity references.
        /// </summary>
        /// <param name="fileReferences">The file references.</param>
        private static void UpdateUnmanagedReferences(Collection<FileReference> fileReferences)
        {
            Collection<FileReference> usedReferences = new Collection<FileReference>();
            foreach (FileReference file in fileReferences)
            {
                if (file.ContainsActivity && !usedReferences.Contains(file))
                {
                    if (!usedReferences.Any(f => f.Checksum != null && f.Checksum.Equals(file.Checksum)))
                    {
                        usedReferences.Add(file);
                    }
                    FileReference.GetAllAssociatedFiles(file, fileReferences, usedReferences);
                }
            }

            Collection<FileReference> UnUsedReferences = new Collection<FileReference>();
            foreach (FileReference file in fileReferences)
            {
                if (!Helper.IsWorkflowFile(file.FilePath)
                    && !ActivityComposer.IsTypeInitializer(file.FilePath) 
                    && !usedReferences.Any(f => f.Checksum != null && f.Checksum.Equals(file.Checksum)))
                {
                    UnUsedReferences.Add(file);
                }
            }

            foreach (FileReference file in fileReferences)
            {
                if (file.ContainsActivity)
                {
                    foreach (FileReference unUsedFile in UnUsedReferences)
                    {
                        file.References.Add(unUsedFile.FilePath);
                        file.DisplayReferences.Add(Helper.GetFileName(unUsedFile.FilePath));
                    }
                }
            }
        }

        #endregion
    }

    /// <summary>
    /// Converter to negate the boolean value.
    /// </summary>
    public class NegateBooleanValueConverter : IValueConverter
    {
        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool? boolToConvert = value as bool?;
            if (boolToConvert.HasValue)
            {
                return !boolToConvert.Value;
            }
            return true;
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old value.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
    }
}